/**
 * Copyright 2019 吉鼎科技.
 *
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetBpmTaskRequestMessage;
import cn.easyplatform.messages.request.GetBpmViewRequestMessage;
import cn.easyplatform.messages.vos.BpmTaskVo;
import cn.easyplatform.messages.vos.BpmViewVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Widget;
import cn.easyplatform.web.ext.ZkExt;
import cn.easyplatform.web.ext.bpm.Bpmdesigner;
import cn.easyplatform.web.ext.bpm.event.NodeEvent;
import cn.easyplatform.web.ext.bpm.event.NodeEvent.NodeVo;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.event.EventListenerHandler;
import cn.easyplatform.web.task.support.ManagedComponent;
import cn.easyplatform.web.utils.PageUtils;
import org.dom4j.*;
import org.snaker.engine.helper.SnakerHelper;
import org.snaker.engine.model.ProcessModel;
import org.snaker.engine.parser.ModelParser;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listgroup;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Treeitem;

import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class BpmdesignerBuilder implements ComponentBuilder, ManagedComponent,
        Widget, EventListener<Event> {

    private OperableHandler main;

    private Bpmdesigner bv;

    private String selectNode;

    public BpmdesignerBuilder(OperableHandler main, ZkExt bv) {
        this.main = main;
        this.bv = (Bpmdesigner) bv;
    }

    @Override
    public Component build() {
        if (!bv.isEditable() && Strings.isBlank(bv.getProcessId())
                && Strings.isBlank(bv.getOrderId()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<bpmdesigner>",
                    "processId|orderId");
        bv.setAttribute("$proxy", this);
        if (!Strings.isBlank(bv.getProcessId())
                || !Strings.isBlank(bv.getOrderId())) {
            ComponentService cs = ServiceLocator
                    .lookup(ComponentService.class);
            BpmViewVo bvv = new BpmViewVo();
            if (Strings.isBlank(bv.getOrderId())) {
                bvv.setId(bv.getProcessId());
                bvv.setProcessId(true);
            } else
                bvv.setId(bv.getOrderId());
            IResponseMessage<?> resp = cs
                    .getBpmView(new GetBpmViewRequestMessage(bvv));
            if (resp.isSuccess()) {
                String[] views = (String[]) resp.getBody();
                bv.setModel(views[0]);
                if (views[1] != null) {
                    bv.setTask(views[1]);
                    bv.addEventListener(NodeEvent.ON_NODE, this);
                } else
                    bv.setTask("{'activeRects':{'rects':[{'paths':[],'name':''}]}}");
                if (bv.isEditable())
                    bv.addEventListener(Events.ON_OPEN, this);
            } else
                Clients.wrongValue(bv, (String) resp.getBody());
        } else {
            bv.setModel("{}");
            bv.setTask("{'activeRects':{'rects':[{'paths':[],'name':''}]}}");
            bv.addEventListener(Events.ON_OPEN, this);
        }
        if (bv.isEditable() && !Strings.isBlank(bv.getEvent()))
            PageUtils.addEventListener(main, Bpmdesigner.ON_SAVE, bv);
        return bv;
    }

    @Override
    public void onEvent(Event event) throws Exception {
        if (event.getName().equals(Events.ON_OPEN)) {
            @SuppressWarnings("unchecked")
            Map<String, Object> data = (Map<String, Object>) event.getData();
            String type = (String) data.get("type");
            selectNode = (String) data.get("name");
            String evt = null;
            if (type.equals("task")) {// 功能选择
                if (!Strings.isBlank(bv.getTaskEvent()))
                    evt = bv.getTaskEvent();
            } else if (type.equals("decision")) {// 分支条件
                if (!Strings.isBlank(bv.getDecisionEvent()))
                    evt = bv.getDecisionEvent();
            } else if (type.equals("assignee")) {// 任务参与者
                if (!Strings.isBlank(bv.getAssigneeEvent()))
                    evt = bv.getAssigneeEvent();
            } else if (type.equals("assigneeType")) {//处理类型
                if (!Strings.isBlank(bv.getAssigneeTypeEvent()))
                    evt = bv.getAssigneeTypeEvent();
            } else if (type.equals("assigneeRole")) {// 参与角色
                if (!Strings.isBlank(bv.getAssigneeRoleEvent()))
                    evt = bv.getAssigneeRoleEvent();
            }
            if (evt != null) {
                EventListenerHandler elh = new EventListenerHandler(event.getName(), main, evt);
                elh.onEvent(event);
            }
        } else if (event.getName().equals(NodeEvent.ON_NODE)) {
            NodeVo nv = (NodeVo) event.getData();
            ComponentService cs = ServiceLocator
                    .lookup(ComponentService.class);
            IResponseMessage<?> resp = cs
                    .getBpmTask(new GetBpmTaskRequestMessage(nv.getNodeName(),
                            bv.getOrderId()));
            if (resp.isSuccess()) {
                BpmTaskVo tv = (BpmTaskVo) resp.getBody();
                StringBuilder sb = new StringBuilder();
                sb.append("{")
                        .append("\"name\":\"")
                        .append(nv.getNodeName())
                        .append("\",")
                        .append("\"actors\":\"")
                        .append(tv.getActors() == null ? "" : tv.getActors())
                        .append("\",\"createTime\":\"")
                        .append(tv.getCreateTime() == null ? "" : tv
                                .getCreateTime())
                        .append("\",\"finishTime\":\"")
                        .append(tv.getFinishTime() == null ? "" : tv
                                .getFinishTime()).append("\"}");
                bv.setNodeState(sb.toString());
                sb = null;
            }
        }
    }

    public void setTask(Object taskId) {
        bv.setInputValue(taskId);
    }

    public void setAssignee(Collection<?> selItems) {
        if (selItems.isEmpty())
            return;
        StringBuilder sb = new StringBuilder();
        Iterator<?> itr = selItems.iterator();
        while (itr.hasNext()) {
            Object o = itr.next();
            if (o instanceof Listitem) {
                Object val = ((Listitem) o).getValue();
                if (val instanceof ListRowVo) {
                    ListRowVo rv = (ListRowVo) val;
                    sb.append(rv.getData()[0]);
                } else if (val instanceof FieldVo[]) {
                    FieldVo[] fvs = (FieldVo[]) val;
                    sb.append(fvs[0].getValue());
                }
            } else {
                ListRowVo rv = ((Treeitem) o).getValue();
                if (rv != null)
                    sb.append(rv.getData()[0]);
            }
            if (itr.hasNext())
                sb.append(",");
        }
        bv.setInputValue(sb.toString());
        sb = null;
    }

    @SuppressWarnings("unchecked")
    public void setDecision(Listbox box, boolean purge) {
        if (box.getSelectedCount() <= 0)
            return;
        Listgroup group = null;
        for (Listgroup g : box.getGroups()) {
            if (g.isSelected()) {
                group = g;
                break;
            }
        }
        if (group == null) {
            // 反推group
            for (int i = box.getSelectedIndex() - 1; i >= 0; i--) {
                Component c = box.getItemAtIndex(i);
                if (c instanceof Listgroup) {
                    group = (Listgroup) c;
                    break;
                }
            }
        }
        int count = 0;
        for (Listitem item : group.getItems()) {
            if (item.isSelected())
                count++;
        }
        if (count == 0)
            return;
        Document doc = null;
        try {
            doc = DocumentHelper.parseText((String) bv.getValue());
            Element root = doc.getRootElement();
            Element node = null;
            for (Iterator<Element> i = root.elementIterator("decision"); i
                    .hasNext(); ) {
                Element elm = i.next();
                if (elm.attributeValue("name").equals(selectNode)) {
                    node = elm;
                    break;
                }
            }
            if (node != null) {
                // 设置表达式
                Attribute attr = node.attribute("expr");
                FieldVo[] fvs = group.getValue();
                if (attr == null)
                    node.addAttribute("expr", (String) fvs[0].getValue());
                else
                    attr.setText((String) fvs[0].getValue());
                if (purge) {
                    // 清除关连的功能
                    if (node.hasContent()) {
                        List<String> toTasks = new ArrayList<String>();
                        for (Iterator<Element> i = node
                                .elementIterator("transition"); i.hasNext(); ) {
                            Element elm = i.next();
                            String to = elm.attributeValue("to");
                            toTasks.add(to);
                            i.remove();
                        }
                        for (Iterator<Element> i = root.elementIterator("task"); i
                                .hasNext(); ) {
                            Element elm = i.next();
                            boolean f = false;
                            for (String to : toTasks) {
                                if (elm.attributeValue("name").equals(to)) {
                                    f = true;
                                    break;
                                }
                            }
                            if (f)
                                i.remove();
                        }
                    }
                }
                String[] layout = node.attributeValue("layout").split(",");
                int x = Integer.parseInt(layout[0]) + 120;
                int y = Integer.parseInt(layout[1]);
                // 设置新的分支
                for (Listitem item : group.getItems()) {
                    if (item.isSelected()) {
                        fvs = item.getValue();
                        String name = (String) fvs[0].getValue();
                        String displayName = (String) fvs[1].getValue();
                        String tofunc = (String) fvs[2].getValue();
                        String tofuncname = (String) fvs[3].getValue();
                        String tofuncdispname = (String) fvs[4].getValue();
                        Element transition = node.addElement("transition");
                        transition.addAttribute("displayName", displayName);
                        transition.addAttribute("name", name);
                        transition.addAttribute("to", tofuncname);
                        if (Strings.isBlank(tofunc) || tofunc.equalsIgnoreCase("end")) {// 直接连到end
                            Node e = root.selectSingleNode("//end");
                            if (e == null) {// 没有end
                                Element end = root.addElement("end");
                                end.addAttribute("name", tofuncname);
                                end.addAttribute("displayName", tofuncname);
                                end.addAttribute("layout", x + "," + y
                                        + ",-1,-1");
                            } else
                                transition.addAttribute("to", ((Element) e).attributeValue("name"));
                        } else {
                            boolean f = false;
                            for (Iterator<Element> i = root
                                    .elementIterator("task"); i.hasNext(); ) {
                                Element elm = i.next();
                                if (elm.attributeValue("name").equals(
                                        tofuncname)) {
                                    f = true;
                                    break;
                                }
                            }
                            if (!f) {
                                Element task = root.addElement("task");
                                task.addAttribute("name", tofuncname);
                                task.addAttribute("displayName", tofuncdispname);
                                task.addAttribute("form", tofunc);
                                task.addAttribute("layout", x + "," + y
                                        + ",-1,-1");
                                task.addAttribute("performType", "ANY");
                            }
                        }
                        y += 100;
                    }
                }
                String xml = doc.asXML();
                bv.setValue(xml);
                ProcessModel model = ModelParser.parse(xml.getBytes("utf-8"));
                bv.setModel(SnakerHelper.getModelJson(model));
            }
        } catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
    }

    @Override
    public Component getComponent() {
        return bv;
    }

    @Override
    public void reload(Component rg) {
        try {
            String val = (String) bv.getValue();
            if (!Strings.isBlank(val)) {
                ProcessModel model = ModelParser.parse(((String) bv.getValue())
                        .getBytes("utf-8"));
                bv.setModel(SnakerHelper.getModelJson(model));
            }
        } catch (UnsupportedEncodingException e) {
        }
    }
}
